﻿using System;
using System.Collections;
using System.Xml;
using System.Text;
using InfoJetSoft.Service.Entity;
using InfoJetSoft.Service.Util;

namespace InfoJetSoft.Service.Worker
{
	
	//负责分析XmlToEdit。
	class XSNManifestXmlToEditParseWorker
	{
		
		public static void  parseXmlToEdit(Manifest manifest)
		{
			XmlElement root = manifest.Doc.DocumentElement;
			string xsfPrefix = manifest.XsfPrefix;
			
			string xmlToEditFilter = "//" + NamespaceUtil.toFullName(xsfPrefix, "xmlToEdit");
			XmlNodeList xmlToEditElementList = DOMUtil.selectNodes(root, xmlToEditFilter, 0, manifest.NsResolver);
			ArrayList xmlToEditList = new ArrayList();
			foreach ( XmlNode xmlToEditNode in xmlToEditElementList ) 
			{
				XmlElement xmlToEditElement = (XmlElement) xmlToEditNode;
				XmlToEdit xmlToEdit = new XmlToEdit();
				xmlToEdit.Name = xmlToEditElement.GetAttribute("name");
				xmlToEdit.Item = xmlToEditElement.GetAttribute("item");
				xmlToEdit.ViewContext = xmlToEditElement.GetAttribute("viewContext");
				if (xmlToEdit.Item.Length <= 0)
				{
					throw new InfoJetException("xmlToEdit no item", "XXP01");
				}
				xmlToEdit.Item = PathUtil.formularPath(xmlToEdit.Item);
				xmlToEdit.Container = xmlToEditElement.GetAttribute("container");
				if (xmlToEdit.Container.Length > 0)
				{
					xmlToEdit.Container = PathUtil.formularPath(xmlToEdit.Container);
				}
				XSNManifestXmlToEditParseWorker.parseEditWith(xmlToEditElement, xmlToEdit, manifest);

				XmlElement viewElement = (XmlElement)xmlToEditElement.SelectSingleNode( "../.." );
				xmlToEdit.ViewName = viewElement.GetAttribute("name");

				xmlToEditList.Add(xmlToEdit);
			}
			
			manifest.XmlToEditList = xmlToEditList;
		}

		private static void parseEditWith(XmlElement xmlToEditElement, XmlToEdit xmlToEdit, Manifest manifest)
		{
			string xsfPrefix = manifest.XsfPrefix;
			string editWithFilter = NamespaceUtil.toFullName(xsfPrefix, "editWith");
			XmlElement editWithElement = (XmlElement) DOMUtil.selectANode(xmlToEditElement, editWithFilter, manifest.NsResolver);
			if (editWithElement == null)
			{
				return ;
			}
			xmlToEdit.Caption = editWithElement.GetAttribute("caption");
			xmlToEdit.Component = editWithElement.GetAttribute("component");
			xmlToEdit.Type = editWithElement.GetAttribute("type");
			//CID 0495 //2010.02.05 //Begin
			xmlToEdit.AllowedFileTypes = editWithElement.GetAttribute( "allowedFileTypes" );
			//CID 0495 //2010.02.05 //End
			//CID 0517 //2010.03.24 //Begin
			string proofing = editWithElement.GetAttribute( "proofing" );
			if( proofing != null && proofing.Equals( "no" ) )
			{
				xmlToEdit.Proofing = false;
			}
			//CID 0517 //2010.03.24 //End
			if (xmlToEdit.Type.Length <= 0)
			{
				xmlToEdit.Type = JetConstant.xField_Plain;
			}
			string removeAncestorsValue = editWithElement.GetAttribute("removeAncestors");
			if (removeAncestorsValue.Length <= 0)
			{
				removeAncestorsValue = "0";
			}
			if (xmlToEdit.Component.Equals(JetConstant.CMP_xReplace))
			{
				removeAncestorsValue = "0";
			}
			try
			{
				xmlToEdit.RemoveAncestors = System.Int32.Parse(removeAncestorsValue);
			}
			catch (System.Exception e)
			{
				throw new InfoJetException("removeAncestors is not an integer", "XXP01");
			}
			string maxLengthValue = editWithElement.GetAttribute("maxLength");
			if (maxLengthValue.Length <= 0)
			{
				maxLengthValue = "0";
			}
			try
			{
				xmlToEdit.MaxLength = System.Int32.Parse(maxLengthValue);
			}
			catch (System.Exception e)
			{
				throw new InfoJetException("maxLength is not an integer", "XXP07");
			}
			parseChooseFragmentList(editWithElement, xmlToEdit, manifest);
		}
		
		private static void  parseChooseFragmentList(XmlElement editWithElement, XmlToEdit xmlToEdit, Manifest manifest)
		{
			string xsfPrefix = manifest.XsfPrefix;
			StringBuilder filterBuffer = new StringBuilder();
			filterBuffer.Append(NamespaceUtil.toFullName(xsfPrefix, "fragmentToInsert"));
			filterBuffer.Append("/");
			filterBuffer.Append(NamespaceUtil.toFullName(xsfPrefix, "chooseFragment"));
			XmlNodeList chooseFragmentElementList = DOMUtil.selectNodes(editWithElement, filterBuffer.ToString(), 0, manifest.NsResolver);
			ArrayList chooseFragmentList = new ArrayList();
			foreach ( XmlNode chooseFragmentNode in chooseFragmentElementList ) 
			{
				XmlElement chooseFragmentElement = (XmlElement) chooseFragmentNode;
				if (manifest.SolutionFormatVersion.Equals(JetConstant.VER_2003))
				{
					parse2003ChooseFragment(manifest, chooseFragmentList, chooseFragmentElement);
				}
				//else if (manifest.SolutionFormatVersion.Equals(JetConstant.VER_2003SP1))
				else
				{
					parse2003SP1ChooseFragment(manifest, xmlToEdit, chooseFragmentList, chooseFragmentElement);
				}
			}
			xmlToEdit.ChooseFragmentList = chooseFragmentList;
		}
		
		private static void parse2003ChooseFragment(Manifest manifest, ArrayList chooseFragmentList, XmlElement chooseFragmentElement)
		{
			string xsfPrefix = manifest.XsfPrefix;
			string parent = chooseFragmentElement.GetAttribute("parent");
			if (parent.Length <= 0)
			{
				parent = ".";
			}
			ChooseFragment chooseFragment = new ChooseFragment();
			chooseFragment.Parent = parent;
			ArrayList nodeList = new ArrayList();
			foreach ( XmlNode node in chooseFragmentElement.ChildNodes ) 
			{
				if (node is XmlElement)
				{
					XmlElement element = (XmlElement) node;
					if (element.Name.Equals(NamespaceUtil.toFullName(xsfPrefix, "attributeName")))
					{
						XmlAttribute attribute = XSNManifestXmlToEditParseWorker.buildAttributeByData(element, manifest);
						nodeList.Add(attribute);
					}
					else
					{
						nodeList.Add(element.CloneNode(true));
					}
				}
			}
			chooseFragment.XmlNodeList = nodeList;
			chooseFragmentList.Add(chooseFragment);
		}
		
		private static void  parse2003SP1ChooseFragment(Manifest manifest, XmlToEdit xmlToEdit, ArrayList chooseFragmentList, XmlElement chooseFragmentElement)
		{
			string xsfPrefix = manifest.XsfPrefix;
			string parent = chooseFragmentElement.GetAttribute("parent");
			string innerFragment = chooseFragmentElement.GetAttribute("innerFragment");
			if (innerFragment.Length <= 0)
			{
				if (!xmlToEdit.Component.Equals(JetConstant.CMP_xReplace))
				{
					throw new InfoJetException("No innerFragment.", "XXP05");
				}
				else
				{
					innerFragment = "./*";
				}
			}
			XmlNodeList dataElementList = DOMUtil.selectNodes(chooseFragmentElement, innerFragment, 0, manifest.NsResolver);
			if (dataElementList.Count <= 0)
			{
				throw new InfoJetException("No innerFragment data.", "XXP07");
			}
			XmlNode firstNode = (XmlNode) dataElementList[ 0 ];
			if (!(firstNode is XmlElement))
			{
				throw new InfoJetException("Invalidate innerFragment: " + innerFragment, "XXP08");
			}
			XmlElement dataElement = (XmlElement) firstNode;
			if (!isParentElement(chooseFragmentElement, dataElement))
			{
				throw new InfoJetException("Invalidate innerFragment: " + innerFragment, "XXP09");
			}
			while (!dataElement.Equals(chooseFragmentElement))
			{
				ChooseFragment chooseFragment = new ChooseFragment();
				if (parent.Length > 0)
				{
					if( innerFragment != null && innerFragment.Length > 0 ){
						chooseFragment.Parent = parent + "/" + getParentPath(chooseFragmentElement, dataElement);
					}else{
						chooseFragment.Parent = parent;
					}
				}
				else
				{
					chooseFragment.Parent = getParentPath(chooseFragmentElement, dataElement);
				}
				ArrayList nodeList = new ArrayList();
				foreach ( XmlNode node in chooseFragmentElement.ChildNodes ) 
				{
					if (node is XmlElement)
					{
						XmlElement element = (XmlElement) node;
						if (element.Name.Equals(NamespaceUtil.toFullName(xsfPrefix, "attributeName")))
						{
							XmlAttribute attribute = XSNManifestXmlToEditParseWorker.buildAttributeByData(element, manifest);
							//这里没有检查attribute重复的情况.
							nodeList.Add(attribute);
						}
					}
				}
				nodeList.Add(dataElement.CloneNode(true));
				chooseFragment.XmlNodeList = nodeList;
				chooseFragmentList.Add(chooseFragment);
				
				dataElement = (XmlElement) dataElement.ParentNode;
			}
		}
		
		private static ChooseFragment buildChooseFragment(Manifest manifest, XmlElement chooseFragmentElement, string parent)
		{
			string xsfPrefix = manifest.XsfPrefix;
			ChooseFragment chooseFragment = new ChooseFragment();
			chooseFragment.Parent = parent;
			ArrayList nodeList = new ArrayList();
			foreach ( XmlNode node in chooseFragmentElement.ChildNodes ) 
			{
				if (node is XmlElement)
				{
					XmlElement element = (XmlElement) node;
					if (element.Name.Equals(NamespaceUtil.toFullName(xsfPrefix, "attributeName")))
					{
						XmlAttribute attribute = XSNManifestXmlToEditParseWorker.buildAttributeByData(element, manifest);
						nodeList.Add(attribute);
					}
					else
					{
						nodeList.Add(element.CloneNode(true));
					}
				}
			}
			chooseFragment.XmlNodeList = nodeList;
			return chooseFragment;
		}
		
		private static XmlAttribute buildAttributeByData(XmlElement attrDataElement, Manifest manifest)
		{
			XmlAttribute attribute = null;
			string name = attrDataElement.GetAttribute("attribute");
			string value_Renamed = attrDataElement.GetAttribute("value");
			if (name.Length <= 0)
			{
				throw new InfoJetException("attributeData no name", "XXP02");
			}
			XmlDocument doc = manifest.Doc;
			StringTokenizer nameTokenizer = new StringTokenizer(name, ":");
			if (nameTokenizer.Count == 1)
			{
				string uri = manifest.NsResolver.GetURI("");
				if (uri != null)
				{
					attribute = (XmlAttribute) doc.CreateAttribute(name, uri);
					attribute.Value = value_Renamed;
				}
				else
				{
					attribute = (XmlAttribute) doc.CreateAttribute(name);
					attribute.Value = value_Renamed;
				}
			}
			else if (nameTokenizer.Count == 2)
			{
				string prefix = nameTokenizer.NextToken();
				name = nameTokenizer.NextToken();
				string uri = manifest.NsResolver.GetURI(prefix);
				if (uri == null)
				{
					throw new InfoJetException("attributeData name used invalidate namespace", "XXP03");
				}
				else
				{
					attribute = doc.CreateAttribute( prefix , name , uri );
					attribute.Value = value_Renamed;
				}
			}
			else
			{
				throw new InfoJetException("attributeData error format", "XXP04");
			}
			return attribute;
		}
		
		private static bool isParentElement(XmlElement parentElement, XmlElement childElement)
		{
			XmlNode ownerNode = childElement.ParentNode;
			while (ownerNode != null)
			{
				if (ownerNode is XmlElement)
				{
					if (ownerNode.Equals(parentElement))
					{
						return true;
					}
					else
					{
						ownerNode = ownerNode.ParentNode;
					}
				}
				else
				{
					return false;
				}
			}
			return false;
		}
		
		private static string getParentPath(XmlElement chooseFragmentElement, XmlElement dataElement)
		{
			StringBuilder parentBuffer = new StringBuilder();
			XmlElement parentElement = (XmlElement) dataElement.ParentNode;
			while (!parentElement.Equals(chooseFragmentElement))
			{
				parentBuffer.Insert(0, "/" + parentElement.Name);
				parentElement = (XmlElement) parentElement.ParentNode;
			}
			if (parentBuffer.Length > 0)
			{
				if (parentBuffer[0] == '/')
				{
					parentBuffer.Remove(0, 1);
				}
			}
			else
			{
				parentBuffer.Append(".");
			}
			return parentBuffer.ToString();
		}
	}
}